/*	MultiFileChooserPanel.java
 *
 *	Copyright (c) Manateem Challenge Group, Zaia Group, and contributors 2010
 *
 *	This file is part of MSGSwingSet ('this project').
 *
 *	This project is free software: you can redistribute it and/or modify it
 *	under the terms of the GNU General Public License as published by the Free
 *	Software Foundation, either version 3 of the License, or (at your option)
 *	any later version.
 *
 *	This project is distributed in the hope that it will be useful, but WITHOUT
 *	ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 *	FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 *	more details.
 *
 *	You should have received a copy of the GNU General Public License along with
 *	this project. If not, see <http://www.gnu.org/licenses/>.
 */
package edu.bu.manateem.msg.gui.component;

// Imports
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import java.util.Set;
import java.util.prefs.Preferences;

import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.filechooser.FileFilter;

import edu.bu.manateem.msg.MSGConstants;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 *	<p>
 *	A simple GUI for a multiple file chooser. Allows the user to select files
 *	(purpose left to concrete subclasses) across multiple locations. Users may
 *	also de-select files they have previously selected.
 *	</p>
 *
 *
 *	@author
 *		Christopher Jacobs
 *
 *	@version
 *		1.0
 *
 *	@changelog
 *		May 13, 2010	- Version 1.0.
 */
public abstract class MultiFileChooserPanel
	extends JPanel
	implements ActionListener
{
	/*
	 * TYPES
	 */

	/*
	 * CONSTANTS
	 */

	// Serialize
	private static final long serialVersionUID = 1L;
	// Logging
	private static final Logger LOG =
		LoggerFactory.getLogger(MultiFileChooserPanel.class);
	
	// actions
	private static final String ADD = "add";
	private static final String REMOVE = "remove";
	private static final String LAST_DIR = "last_dir";
	
	/*
	 * FIELDS
	 */

	private JTable fileTable = new JTable(new FileChooserTableModel());
	private JButton addButton = new JButton("Add File(s)");
	private JButton removeButton = new JButton("Remove File(s)");
	private Preferences preferences =
		Preferences.systemNodeForPackage(this.getClass());
	private JFileChooser fileChooser =
		new JFileChooser(preferences.get(MultiFileChooserPanel.LAST_DIR,
			MSGConstants.USER_DIR));
	
	/*
	 * CONSTRUCTORS
	 */

	public MultiFileChooserPanel()
	{
		super();
		
		MultiFileChooserPanel.LOG.debug("init class");
		initFileTable();
		initFileChooser();
		init();
		
	}
	
	/*
	 * INITIALIZERS
	 */

	/*
	 * METHODS
	 */

	/**
	 *	<p>
	 *	Invoked when an action occurs.
	 *	</p>
	 *
	 *	@since
	 *		1.0
	 */
	@Override
	public void actionPerformed(ActionEvent e)
	{
		MultiFileChooserPanel.LOG.debug("button pressed for action -- {}",
			e.getActionCommand());
		String command = e.getActionCommand();
		if (command.equals(MultiFileChooserPanel.ADD))
		{
			MultiFileChooserPanel.LOG.debug("open add dialog");
			if (showDialog(fileChooser) == JFileChooser.APPROVE_OPTION)
			{
				MultiFileChooserPanel.LOG.info("add files");
				try
				{
					FileChooserTableModel model =
						(FileChooserTableModel) fileTable.getModel();
					model.addFiles(fileChooser.getSelectedFiles());
					
					// remember the directory of the 1st selected file
					preferences.put(MultiFileChooserPanel.LAST_DIR,
						fileChooser.getSelectedFiles()[0].getCanonicalPath());
				}
				catch (IOException x)
				{
					MultiFileChooserPanel.LOG.error("i/o error: {}",
						x.getLocalizedMessage());
				}
			}
		}
		else if (command.equals(MultiFileChooserPanel.REMOVE))
		{
			MultiFileChooserPanel.LOG.info("remove files");
			clear();
		}
	}
	
	public void addFile(String filePath)
	{
		try
		{
			FileChooserTableModel model =
				(FileChooserTableModel) fileTable.getModel();
			model.addFile(new File(filePath));
		}
		catch (IOException x)
		{
			MultiFileChooserPanel.LOG.error("i/o error: {}",
				x.getLocalizedMessage());
		}
	}
	
	public void clear()
	{
		FileChooserTableModel model =
			(FileChooserTableModel) fileTable.getModel();
		model.removeRows(fileTable.getSelectedRows());
	}
	
	public void clearAll()
	{
		fileTable.selectAll();
		clear();
	}
	
	/**
	 *	<p>
	 *	Gets the canonical paths to the files selected as a <code>Set</code>.
	 *	</p>
	 *
	 *	@since
	 *		1.0
	 *
	 *
	 *	@return
	 *		the <code>Set</code> of selected files.
	 */
	public Set<String> getCanonicalPaths()
	{
		MultiFileChooserPanel.LOG.debug("getting canonical paths");
		FileChooserTableModel model =
			(FileChooserTableModel) fileTable.getModel();
		return model.getCanonicalPaths();
	}
	
	/**
	 *	<p>
	 *	Initializes the "master" frame of <code>this</code> object. Sets the
	 *	default panel behaviors and layout, and packs components.
	 *	</p>
	 *
	 *	@since
	 *		1.0
	 */
	private void init()
	{
		MultiFileChooserPanel.LOG.debug("build Container");
		
		// behavior
		setVisible(true);
		
		// layout
		GridBagLayout layout = new GridBagLayout();
		setLayout(layout);
		
		GridBagConstraints constraints = new GridBagConstraints();
		
		constraints.anchor = GridBagConstraints.PAGE_START;
		constraints.fill = GridBagConstraints.HORIZONTAL;
		constraints.gridwidth = 3;
		constraints.gridx = 0;
		constraints.gridy = 0;
		constraints.weightx = 0.5;
		add(fileTable.getTableHeader(),constraints);
		
		constraints.gridy = 1;
		add(fileTable,constraints);
		
		constraints.fill = GridBagConstraints.NONE;
		constraints.insets = new Insets(5,0,0,0);
		constraints.gridwidth = 1;
		constraints.weightx = 0.0;
		
		constraints.anchor = GridBagConstraints.LAST_LINE_START;
		constraints.gridy = 2;
		constraints.gridwidth = 1;
		add(addButton,constraints);
		
		constraints.anchor = GridBagConstraints.LAST_LINE_END;
		constraints.gridx = 2;
		add(removeButton,constraints);
		
		// finish
		MultiFileChooserPanel.LOG.debug("build Container -- DONE");
	}
	
	/**
	 *	<p>
	 *	Initializes the file chooser (browser), and its directly associated
	 *	components for <code>this</code> object.
	 *	</p>
	 *
	 *	@since
	 *		1.0
	 */
	private void initFileChooser()
	{
		MultiFileChooserPanel.LOG.debug("build file chooser");
		fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
		fileChooser.setMultiSelectionEnabled(true);
		MultiFileChooserPanel.LOG.debug("build file chooser -- DONE");
	}
	
	/**
	 *	<p>
	 *	Initializes the file chooser (browser), and its directly associated
	 *	components for <code>this</code> object.
	 *	</p>
	 *
	 *	@since
	 *		1.0
	 */
	private void initFileTable()
	{
		// accessories
		MultiFileChooserPanel.LOG.debug("build add button");
		addButton.addActionListener(this);
		addButton.setActionCommand(MultiFileChooserPanel.ADD);
		MultiFileChooserPanel.LOG.debug("build add button -- DONE");
		
		MultiFileChooserPanel.LOG.debug("build remove button");
		removeButton.addActionListener(this);
		removeButton.setActionCommand(MultiFileChooserPanel.REMOVE);
		MultiFileChooserPanel.LOG.debug("build remove button -- done");
		
		// table
		MultiFileChooserPanel.LOG.debug("build file table");
		MultiFileChooserPanel.LOG.debug("build file table -- DONE");
	}
	
	/**
	 *	<p>
	 *	Sets the current file filter.
	 *	</p>
	 *
	 *	@see
	 *		javax.swing.JFileChooser#setFileFilter(FileFilter)
	 *
	 *	@since
	 *		1.0
	 *
	 *
	 *	@param
	 *		description	- description of this file filter (appears in dialog)
	 *	@param
	 *		allowableExtensions	- the file extensions to filter for
	 */
	public void setFileFilter(final String description,
		final String...allowableExtensions)
	{
		MultiFileChooserPanel.LOG.debug("setting file filter");
		fileChooser.setAcceptAllFileFilterUsed(false);
		fileChooser.setFileFilter(new FileFilter()
		{
			@Override
			public boolean accept(File file)
			{
				if (file.isDirectory())
				{
					return true;
				}
				
				for (String extension : allowableExtensions)
				{
					if (file.getName().toLowerCase().endsWith(
						"." + extension.toLowerCase()))
					{
						return true;
					}
				}
				return false;
			}
			
			/**
			 *	<p>
			 *	The description of this filter.
			 *	</p>
			 *
			 *	@since
			 *		1.0
			 */
			@Override
			public String getDescription()
			{
				return description;
			}
		});
	}
	
	/**
	 *	<p>
	 *	Shows the appropriate dialog for the file chooser type. File chooser
	 *	type is determined by inheriting classes based on their functionality
	 *	(e.g. saving, loading,etc.).
	 *	</p>
	 *
	 *	@since
	 *		1.0
	 */
	public abstract int showDialog(JFileChooser chooser);
}
